﻿namespace MSSqlSchemaDoc.Provider.MicrosoftSqlServer
{
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Data;
    using MSSqlSchemaDoc.Core;
    using MSSqlSchemaDoc.Core.DataStructures;
    using MSSqlSchemaDoc.Provider.MicrosoftSqlServer.Helpers;

    /// <summary>
    /// Contains the helper methods for columns.
    /// </summary>
    public partial class SqlScriptingManager
    {
        /// <summary>
        /// Construct a column from a data reader.
        /// </summary>
        /// <param name="c">The column for which the options should be set. Passing in an existing column will cause the current data to be added to the column.</param>
        /// <param name="reader">Use the data in the current position of the data reader.</param>
        /// <param name="indexData">Index data.</param>
        /// <param name="foreignKeyData">Foreign key data.</param>
        /// <returns>The constructed column.</returns>
        private static Column ConstructColumn(Column c, IDataReader reader, DataTable indexData, DataTable foreignKeyData)
        {
            c.Name = DataReaderHelper.GetString(reader, "ColumnName");
            c.ColumnId = DataReaderHelper.GetInt(reader, "ColumnId").Value;
            c.ParentObjectId = DataReaderHelper.GetInt(reader, "ObjectId").Value;

            c.DataType = new DataType();
            PropertyBinder.CopyValues(reader, c.DataType, "Name", "TypeName");

            c.IsNullable = DataReaderHelper.GetBool(reader, "IsNullable");
            
            c.HasDefault = DataReaderHelper.GetBool(reader, "HasDefault");
            if (c.HasDefault)
            {
                c.DefaultValue = DataReaderHelper.GetString(reader, "DefaultValue");
            }

            c.IsAnsiPadded = DataReaderHelper.GetBool(reader, "IsAnsiPadded");
            c.IsRowGuid = DataReaderHelper.GetBool(reader, "IsRowGuid");
            c.IsIdentity = DataReaderHelper.GetBool(reader, "IsIdentity");
            c.IsComputed = DataReaderHelper.GetBool(reader, "IsComputed");
            c.IsFullTextIndexed = DataReaderHelper.GetBool(reader, "IsFullTextIndexed");

            // Collation
            string collation = DataReaderHelper.GetString(reader, "Collation");
            if (collation != null)
            {
                c.Collation = collation;
            }

            // Set the identity settings
            if (c.IsIdentity)
            {
                IdentityColumn idc = new IdentityColumn()
                {
                    Seed = DataReaderHelper.GetInt(reader, "IdentitySeed").Value,
                    Increment = DataReaderHelper.GetInt(reader, "IdentityIncrement").Value
                };

                c.IdentitySettings = idc;
            }

            // Computed columns settings
            if (c.IsComputed)
            {
                ComputedColumn cc = new ComputedColumn()
                {
                    Definition = DataReaderHelper.GetString(reader, "ComputationDefinition"),
                    IsPersisted = DataReaderHelper.GetBool(reader, "IsComputationPersisted"),
                    UsesDatabaseCollation = DataReaderHelper.GetBool(reader, "ComputationUsesDatabaseCollation")
                };

                c.ComputationSettings = cc;
            }

            // Key Constriants
            string keyConstraintType = DataReaderHelper.GetString(reader, "KeyConstraintType");
            if (!string.IsNullOrEmpty(keyConstraintType))
            {
                ColumnKeyConstraint ckc = new ColumnKeyConstraint();
                ckc.KeyConstraintType = keyConstraintType;
                ckc.IsSystemNamed = DataReaderHelper.GetBool(reader, "IsKeyConstraintSystemNamed");

                c.ColumnKeyConstraint = ckc;
            }

            // FK Constraints
            AddForeignKeyConstraintsForColumn(c, foreignKeyData);

            // Index Members
            IDictionary<string, IndexMember> indexMembers = new Dictionary<string, IndexMember>();
            if (c.IndexMembers == null)
            {
                c.IndexMembers = new Collection<IndexMember>();
            }
            else
            {
                // Build dictionary of existing index members
                foreach (IndexMember ixm in c.IndexMembers)
                {
                    indexMembers.Add(ixm.Name, ixm);
                }
            }

            int ordinalTableId = reader.GetOrdinal("ObjectId");
            int ordinalColumnId = reader.GetOrdinal("ColumnId");

            for (int i = 0; i < indexData.Rows.Count; i++)
            {
                DataRow indexRow = indexData.Rows[i];

                int tableId = reader.GetInt32(ordinalTableId);
                int columnId = reader.GetInt32(ordinalColumnId);

                if (reader.GetInt32(ordinalTableId) == indexRow.Field<int>("TableId")
                    &&
                    reader.GetInt32(ordinalColumnId) == indexRow.Field<int>("ColumnId"))
                {
                    IndexMember ixm;
                    string name = indexRow.Field<string>("ObjectName");
                    bool isIncluded = indexRow.Field<bool>("IsIncludedColumn");

                    if (indexMembers.ContainsKey(name))
                    {
                        ixm = indexMembers[name];
                        ixm.IsIncluded = isIncluded;
                    }
                    else
                    {
                        ixm = new IndexMember()
                        {
                            Name = name,
                            IsIncluded = isIncluded
                        };
                        c.IndexMembers.Add(ixm);
                    }
                }
            }

            return c;
        }

        /// <summary>
        /// Retrieves the documentation from an existing column.
        /// </summary>
        /// <param name="sourceColumns">The collection of columns to source from.</param>
        /// <param name="name">The name of the column for which to source data.</param>
        /// <returns>The documentation of the column, or an empty string if not found.</returns>
        private static string GetColumnDocumentation(Collection<ColumnDocumentation> sourceColumns, string name)
        {
            foreach (ColumnDocumentation d in sourceColumns)
            {
                if (d.Name == name)
                {
                    return d.Documentation;
                }
            }

            return string.Empty;
        }
    }
}
